package yycore

import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils
import common.Constants._

import utils.Monitor


class WBUStage() extends Module {
  val io = IO(new Bundle() {
    val flush = Input(Bool())
    val in = Flipped(Decoupled(new WBCommitBus))
    val w_reg = Flipped(new GPRWriteIO)
    val bypass = Flipped(new ByPassDataIO)
    val redirect = new RedirectIO
  })

  io := DontCare
  io.in.ready := true.B

  val reset_wb = WireInit(0.U.asTypeOf(new WBCommitBus))
  reset_wb.inst := BUBBLE
  // WriteBack State
  val wb_reg = RegInit(reset_wb)

  // ************************
  // update wb state
  when(io.in.fire() && !io.flush){
    wb_reg := io.in.bits
  }.otherwise{
    wb_reg := reset_wb
  }
  val redirect = wb_reg.redirect


  // ****************************
  // write data back to regfiles
  io.w_reg.rf_wen := wb_reg.instValid && wb_reg.rf_wen
  io.w_reg.w_addr := wb_reg.wb_addr
  io.w_reg.w_data := wb_reg.wb_data

  // **************************
  // bypass
  io.bypass.apply(rf_wen = wb_reg.rf_wen, addr = wb_reg.wb_addr, data = wb_reg.wb_data)

  io.redirect.valid := redirect.valid
  io.redirect.target := redirect.target


  if(!FPGA) {
    //difftest
//    val intr = WireInit(false.B)
//    BoringUtils.addSink(intr, "difftestIntr")
    val commit = wb_reg.instValid //|| intr
    BoringUtils.addSource(RegNext(commit), "difftestCommit")
    BoringUtils.addSource(RegNext(wb_reg.pc), "difftestThisPC")
    BoringUtils.addSource(RegNext(wb_reg.inst), "difftestThisINST")
    BoringUtils.addSource(RegNext(wb_reg.is_mmio), "difftestIsMMIO")
    BoringUtils.addSource(RegNext(wb_reg.inst(1,0) =/= "b11".U), "difftestIsRVC")
    BoringUtils.addSource(RegNext(wb_reg.intrNO), "difftestIntrNO")

    // monitor link
    val instrCnt = RegInit(0.U(DataBits.W))
    when(wb_reg.instValid){
      instrCnt := (instrCnt + 1.U)
    }
    val cycleCnt = WireInit(0.U(DataBits.W))
    BoringUtils.addSink(cycleCnt, "mcycle")
    val mon = Module(new Monitor)
    mon.io.clk := clock
    mon.io.reset := false.B
//      mon.io.isMyCoreTrap := cs_val_inst.
//      mon.io.trapCode := trapCode
    mon.io.trapPC := wb_reg.pc
    mon.io.cycleCnt := cycleCnt
    mon.io.instrCnt := instrCnt

  }


}

